<?php

declare(strict_types=1);

namespace App\Services;

use PDO;
use PDOException;

final class Migrator
{
    public static function run(string $migrationsDir): array
    {
        $pdo = Database::pdo();
        self::ensureTable($pdo);

        $applied = self::getApplied($pdo);
        $files = glob(rtrim($migrationsDir, '/\\') . '/*.sql') ?: [];
        sort($files, SORT_STRING);

        $appliedNow = [];
        foreach ($files as $file) {
            $name = basename($file);
            if (isset($applied[$name])) {
                continue;
            }
            $sql = file_get_contents($file) ?: '';
            if (trim($sql) === '') {
                continue;
            }
            self::execBatch($pdo, $sql);
            self::markApplied($pdo, $name);
            $appliedNow[] = $name;
        }

        return $appliedNow;
    }

    private static function ensureTable(PDO $pdo): void
    {
        $pdo->exec("
            CREATE TABLE IF NOT EXISTS migrations (
              id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
              name VARCHAR(255) NOT NULL UNIQUE,
              applied_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
            ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
        ");
    }

    /**
     * @return array<string, bool> map[name] = true
     */
    private static function getApplied(PDO $pdo): array
    {
        $map = [];
        try {
            $rows = $pdo->query("SELECT name FROM migrations")->fetchAll(PDO::FETCH_COLUMN, 0) ?: [];
            foreach ($rows as $name) {
                $map[(string)$name] = true;
            }
        } catch (PDOException $e) {
            // 若表不存在，ensureTable 会创建，这里忽略
        }
        return $map;
    }

    private static function execBatch(PDO $pdo, string $sql): void
    {
        // MySQL 的 DDL 会触发隐式提交，因此不使用显式事务，逐条执行
        $statements = array_filter(array_map('trim', explode(';', $sql)), function ($s) {
            return $s !== '';
        });

        foreach ($statements as $stmt) {
            $pdo->exec($stmt);
        }
    }

    private static function markApplied(PDO $pdo, string $name): void
    {
        $stmt = $pdo->prepare("INSERT INTO migrations (name) VALUES (:name)");
        $stmt->execute([':name' => $name]);
    }
}
